﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeuralNetworks.Neurons;

namespace NeuralNetworks.Layers
{
    class KohonenLayer : Layer
    {
        private double _learningFactor;
        private double _conscience;
        private int _neighbourhoodDist;
        private int _epochs = 0;
        private double _beta = 1.0; // 1 / #wektorów_treningowych

        private int _x; // pozycja x w 2D warstwie Kohonena
        private int _y; // pozycja y w 2D warstwie Kohonena (w 1D = 0)

        public double LearningFactor
        {
            set { _learningFactor = value; }
        }
        public double Conscience
        {
            set { _conscience = value; }
        }
        public int NeighbourhoodDist
        {
            get { return _neighbourhoodDist; }
            set { _neighbourhoodDist = value; }
        }
        public int Epochs
        {
            get { return _epochs; }
            set { _epochs = value; }
        }
        public int X
        { get { return _x; } }
        public int Y
        { get { return _y; } }

        public KohonenLayer(int x, int y, double learningFactor, double conscience, int nPatterns) : base(x*y, "KL")
        {
            _x = x;
            _y = y;

            for (int i = 0; i < x; i++ )
            {
                for (int j = 0; j < y; j++ )
                {
                    Neurons.Add(new SomNeuron(i, j, x*y));
                }
            }

            _learningFactor = learningFactor;
            _conscience = conscience;
            _beta = 1.0/(double) nPatterns;
            MinRandom = 0.0;
            MaxRandom = 1.0;
        }

        public override void Compute()
        {
            foreach (SomNeuron n in Neurons)
            {
                n.Compute();
            }
        }

        public void Learn(int epochs)
        {
            int winI = GetWinner();
            SomNeuron winner = Neurons[winI] as SomNeuron;
            int winX = winner.X;
            int winY = winner.Y;

            _epochs = epochs;
            foreach (SomNeuron neuron in Neurons)
            {
                neuron.Learn(winX, winY, _epochs, _conscience, _learningFactor, _beta);
            }
        }

        public void AddNeuron(SomNeuron neuron)
        {
            if (neuron == null) { throw (new NeuralNetworksException()); };
            Neurons.Add(neuron);
        }

        public double[] RealOutputs()
        {
            return base.Outputs();
        }

        public override double[] Outputs()
        {
            double[] result = new double[Neurons.Count];
            int winI = GetWinner();

            for (int i = 0; i < Neurons.Count; i++)
            {
                if (i == winI)
                {
                    result[i] = 1.0;
                }
                else
                {
                    result[i] = 0.0;
                }
            }
            return result;
        }

        public int GetWinner()
        {
            SomNeuron n = Neurons[0] as SomNeuron;
            double minimum = n.Output;
            int minI = 0;

            for (int i = 0; i < Neurons.Count; i++)
            {
                n = Neurons[i] as SomNeuron;
                if (n.Output < minimum)
                {
                    minI = i;
                    minimum = n.Output;
                }
            }
            return minI;
        }

    }
}
